package org.jeecg.modules.charge.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.jeecg.common.util.ComUtil;
import org.jeecg.entity.charge.ChargeInvoiceRequest;
import org.jeecg.modules.charge.entity.VoucherEntry;
import org.jeecg.modules.charge.entity.VoucherHead;
import org.jeecg.modules.charge.entity.VoucherInvoiceOffline;
import org.jeecg.modules.charge.mapper.ChargeInvoiceRequestMapper;
import org.jeecg.modules.charge.mapper.ChargeVoucherEntryMapper;
import org.jeecg.modules.charge.mapper.ChargeVoucherHeadMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.*;

/**
 *  电子凭证
  @author: Houms
  @date：      日期：2020年3月20日
  @time:   时间：下午02:11:15
 */

@Service
public class ChargeVoucherService {
	static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger( ChargeVoucherService.class);
    @Autowired
    private  ChargeInvoiceRequestMapper invoiceReqDao;

    @Autowired
    private  ChargeVoucherHeadMapper voucherHeadMapper;

    @Autowired
    private ChargeVoucherEntryMapper  voucherEntryMapper;

    /** 会计凭证流水号(防重复) */
    private static int voucher_node_id_seq = 0;
    private static BigDecimal ONE = new BigDecimal(1);

    /**
     * 生成电子凭证方法
     *
     * */
    public void makeVoucherRecord(String kprqFrom, String kprqTo, String strVoucherDate) {
        try {
            //1.查询发票
            //根据用友返回的yyyyMMddHHmmss格式来查询从月初到当前没有上传过凭证的所有发票
            List<VoucherInvoiceOffline> invoices=invoiceReqDao.getDetailsNoVouRecordByDate(kprqFrom, kprqTo);
            //2.判断是否有需要上传凭证的发票
            //没有需要生成凭证的发票
            if(null==invoices || invoices.size()==0) {
                return ;
            }
            //共通字段
            Date voucherDate = ComUtil.parseDate(strVoucherDate);//凭证日期
            String prepareddate = ComUtil.dateTimeFmt(voucherDate, "yyyy-MM-dd");//制表日期
            String fiscalYear = ComUtil.dateTimeFmt(voucherDate, "yyyy");//会计年度
            String accountingPeriod = ComUtil.dateTimeFmt(voucherDate, "MM");//会计期间
            String lastDate = ComUtil.dateTimeFmt(ComUtil.addTime(new Date(), Calendar.DAY_OF_MONTH, -1), "yyyyMMdd");
            List<VoucherHead> voucherList = new ArrayList<VoucherHead>(); //凭证表格的头部
            Map<VoucherInvoiceOffline, VoucherHead>  invoiceVoucherMap = new HashMap<>();

            for(VoucherInvoiceOffline offline : invoices){
                VoucherHead head = null;
                if(voucherList.isEmpty()){
                    head = new VoucherHead();
                    voucherList.add(head);
                }else{
                    head = voucherList.get(voucherList.size() - 1);
                    if (!head.company.equals(offline.getVoucherCompany())) {//公司变化,新起凭证
                        head = new VoucherHead();
                        voucherList.add(head);
                    }
                }
                //数据组装
                if (ComUtil.isEmpty(head.voucherNodeId)) {
                    head.voucherNodeId = ComUtil.sysDateTime("yyyyMMddHHmmss") + getVoucherNodeIdSeq();
                    head.company = offline.getVoucherCompany();
                    head.voucherType = "记账凭证";//TODO,常量化
                    head.fiscalYear = fiscalYear;
                    head.accountingPeriod = accountingPeriod;
                    head.attachmentNumber = 0;
                    head.prepareddate = prepareddate;
                    head.enter = "物业收费系统";//TODO,常量化
                    head.signature = "N";
                    head.voucherMakingSystem = "总账";//TODO,常量化
                    head.reserve2 = "N";
                }
                invoiceVoucherMap.put(offline, head);//放入Map
                //凭证明细
                    //金额(不含税)的辅助核算
                  String auxiliaryAccountingJe = "[{\"key\":\"物业项目\",\"value\":\"" + offline.getVoucherItem()+ "\"},"
                            + "{\"key\":\"年度\",\"value\":\"" + fiscalYear + "\"}]";
                //税额的辅助核算
                String auxiliaryAccountingSe = "[{\"key\":\"物业项目\",\"value\":\"" + offline.getVoucherItem() + "\"}]";

                //第一个元素为价税合计，而后的元素为按项目两个一组：金额(不含税)、税额
                VoucherEntry voucherJshj = null, voucherJe = null, voucherSe = null;
                if(head.voucherEntryList.isEmpty()){
                    voucherJshj = new VoucherEntry();
                    voucherJe = new VoucherEntry();
                    voucherSe = new VoucherEntry();
                    voucherJshj.entryId = head.voucherEntryList.size() + 1;
                    head.voucherEntryList.add(voucherJshj);
                    voucherJe.entryId = head.voucherEntryList.size() + 1;
                    head.voucherEntryList.add(voucherJe);
                    voucherSe.entryId = head.voucherEntryList.size() + 1;
                    head.voucherEntryList.add(voucherSe);
                }else{
                    voucherJshj = head.voucherEntryList.get(0);
                    voucherJe = head.voucherEntryList.get(head.voucherEntryList.size() - 2);
                    voucherSe = head.voucherEntryList.get(head.voucherEntryList.size() - 1);
                    if (!voucherSe.auxiliaryAccounting.equals(auxiliaryAccountingSe)) {//新项目
                        voucherJe = new VoucherEntry();
                        voucherSe = new VoucherEntry();
                        voucherJe.entryId = head.voucherEntryList.size() + 1;
                        head.voucherEntryList.add(voucherJe);
                        voucherSe.entryId = head.voucherEntryList.size() + 1;
                        head.voucherEntryList.add(voucherSe);
                    }

                }
                //价税合计
                if (ComUtil.isEmpty(voucherJshj.accountCode)) {
                    voucherJshj.accountCode = offline.getFeeMethodCode();
                    voucherJshj.abstracts = "物业费收入";//TODO
                    voucherJshj.currency = "人民币";
                    voucherJshj.unitPrice = BigDecimal.ZERO;
                    voucherJshj.exchangeRate1 = BigDecimal.ZERO;
                    voucherJshj.exchangeRate2 = ONE;
                    voucherJshj.debitQuantity = BigDecimal.ZERO;
                    voucherJshj.primaryDebitAmount = BigDecimal.ZERO;
                    voucherJshj.secondaryDebitAmount = BigDecimal.ZERO;
                    voucherJshj.naturalDebitCurrency = BigDecimal.ZERO;
                    voucherJshj.creditQuantity = BigDecimal.ZERO;
                    voucherJshj.primaryCreditAmount = BigDecimal.ZERO;
                    voucherJshj.secondaryCreditAmount = BigDecimal.ZERO;
                    voucherJshj.naturalCreditCurrency = BigDecimal.ZERO;
                }
                //金额(不含税)
                if (ComUtil.isEmpty(voucherJe.accountCode)) {
                    voucherJe.accountCode = offline.getFeeMethodCode();
                    voucherJe.abstracts = lastDate+":物业费收入";//TODO
                    voucherJe.currency = "人民币";
                    voucherJe.unitPrice = BigDecimal.ZERO;
                    voucherJe.exchangeRate1 = BigDecimal.ZERO;
                    voucherJe.exchangeRate2 = ONE;
                    voucherJe.debitQuantity = BigDecimal.ZERO;
                    voucherJe.primaryDebitAmount = BigDecimal.ZERO;
                    voucherJe.secondaryDebitAmount = BigDecimal.ZERO;
                    voucherJe.naturalDebitCurrency = BigDecimal.ZERO;
                    voucherJe.creditQuantity = BigDecimal.ZERO;
                    voucherJe.primaryCreditAmount = BigDecimal.ZERO;
                    voucherJe.secondaryCreditAmount = BigDecimal.ZERO;
                    voucherJe.naturalCreditCurrency = BigDecimal.ZERO;
                    voucherJe.auxiliaryAccounting = auxiliaryAccountingJe;
                }
                //税额
                if (ComUtil.isEmpty(voucherSe.accountCode)) {
                    voucherSe.accountCode = offline.getFeeMethodCode();
                    voucherSe.abstracts = lastDate+":物业费收入";//TODO
                    voucherSe.currency = "人民币";
                    voucherSe.unitPrice = BigDecimal.ZERO;
                    voucherSe.exchangeRate1 = BigDecimal.ZERO;
                    voucherSe.exchangeRate2 = ONE;
                    voucherSe.debitQuantity = BigDecimal.ZERO;
                    voucherSe.primaryDebitAmount = BigDecimal.ZERO;
                    voucherSe.secondaryDebitAmount = BigDecimal.ZERO;
                    voucherSe.naturalDebitCurrency = BigDecimal.ZERO;
                    voucherSe.creditQuantity = BigDecimal.ZERO;
                    voucherSe.primaryCreditAmount = BigDecimal.ZERO;
                    voucherSe.secondaryCreditAmount = BigDecimal.ZERO;
                    voucherSe.naturalCreditCurrency = BigDecimal.ZERO;
                    voucherSe.auxiliaryAccounting = auxiliaryAccountingSe;
                }
                //累加
                head.attachmentNumber = head.attachmentNumber + 1;
                voucherJshj.primaryDebitAmount = voucherJshj.primaryDebitAmount.add(BigDecimal.valueOf(offline.getMoney()));
                voucherJshj.naturalDebitCurrency = voucherJshj.primaryDebitAmount;
                voucherJe.primaryCreditAmount = voucherJe.primaryCreditAmount.add(BigDecimal.valueOf(offline.getIncome()));
                voucherJe.naturalCreditCurrency = voucherJe.primaryCreditAmount;
                voucherSe.primaryCreditAmount = voucherSe.primaryCreditAmount.add(BigDecimal.valueOf(offline.getTax()));
                voucherSe.naturalCreditCurrency = voucherSe.primaryCreditAmount;

            }
            JSONObject json=new JSONObject();
            json.put("voucherList", voucherList);
            log.info(json.toString());
            //DB处理
            Date sys = new Date();
            //插入凭证
            for (VoucherHead voucherHead : voucherList) {
                Integer count=invoiceReqDao.getCountByComName(kprqFrom, kprqTo, voucherHead.company);
                if(count!=0) {
                    voucherHead.attachmentNumber=count;
                }
                voucherHead.id = String.valueOf(IdWorker.getId());
                voucherHead.voucherId = 0;//0:由NC侧自动分配
                voucherHead.voucherStatus = 0;//凭证状态(0:未上传，8:上传成功，9:上传失败)
                voucherHead.status =1;
                voucherHead.createTime = sys;
                voucherHead.updateTime = sys;
                voucherHeadMapper.insertSelective(voucherHead);
                //明细
                for (VoucherEntry voucherEntry : voucherHead.voucherEntryList) {
                    voucherEntry.headId = voucherHead.id;
                    voucherEntry.status = 1;
                    voucherEntry.createTime = sys;
                    voucherEntry.updateTime = sys;
                    voucherEntryMapper.insertSelectEntry(voucherEntry);
                }
            }
//更新发票里的凭证id
            for (VoucherInvoiceOffline invoice:invoices) {
                ChargeInvoiceRequest detail=new ChargeInvoiceRequest();
                detail.setId(invoice.getId());
                detail.setFiled2(invoiceVoucherMap.get(invoice).id);
                detail.setUpdateTime(sys);
                invoiceReqDao.updateVoucherHeadId(detail);
            }

        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
    }


    //生成凭证记录编号方法
    private static synchronized String getVoucherNodeIdSeq() {
        if (voucher_node_id_seq > 9998) {
            voucher_node_id_seq = 0;
        }
        return String.format("%04d", voucher_node_id_seq++);
    }
/**
 * 电子凭证调用方法
 *
 * */
    public void makeVoucherRecordUtilThisMonthRed() {
        log.info("凭证数据作成。。。");
        Date sys = new Date();
        String strVoucherDate = ComUtil.dateTimeFmt(sys, "yyyyMMdd");
        log.info(strVoucherDate);
        if (strVoucherDate.endsWith("01")) {//月初1日不做凭证（封帐日原因）
            log.info("凭证数据作成。。。结束(月初1日不做凭证)");
            return;
        }
        String kprqFrom = ComUtil.dateTimeFmt(sys, "yyyyMM") + "01000000";//月初开始
        String kprqTo = ComUtil.dateTimeFmt(ComUtil.addTime(sys, Calendar.DAY_OF_MONTH, -1), "yyyyMMdd") + "235959";//昨天为止
        makeVoucherRecord(kprqFrom, kprqTo, strVoucherDate);
    }

    /**
     * 生成XML文件 凭证上传(单个文件)
     */
        public synchronized void uploadVoucherDataOne(String headId) {
        log.info("发票数据上传。。。headId=" + headId);
        VoucherHead voucherHead = voucherHeadMapper.getVoucherHead(headId);
        List<VoucherEntry> entrys=voucherEntryMapper.getVoucherEntryList(headId);
        if(null!=entrys && entrys.size()>0) {
            voucherHead.setVoucherEntryList(entrys);
        }
        voucherHead.setVoucherEntryList(entrys);
        if (voucherHead == null || voucherHead.voucherStatus != 0) {
            log.info("无效id,headId=" + headId);
            return;
        }
        try {
            //生成xml start
            try {
                //XMLUtil
                DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = documentFactory.newDocumentBuilder();
                Document document = builder.newDocument();
                //构造跟节点
                Element ufinterface = document.createElement("ufinterface");
                document.appendChild(ufinterface);
                ufinterface.setAttribute("billtype", "gl");
                ufinterface.setAttribute("replace", "N");
                ufinterface.setAttribute("codeexchanged", "y");
                ufinterface.setAttribute("docid", "docid");//989898989898
                ufinterface.setAttribute("proc", "add");
                ufinterface.setAttribute("receiver", "receiver");
                ufinterface.setAttribute("roottag", "voucher");
                ufinterface.setAttribute("sender", "sender");
                //构造voucher节点
                Element voucher = document.createElement("voucher");
                ufinterface.appendChild(voucher);
                voucher.setAttribute("id", voucherHead.voucherNodeId);

                //构造head
                Element voucher_head = document.createElement("voucher_head");
                voucher.appendChild(voucher_head);
                //构造各子节点
                addElement(document, voucher_head, "company", voucherHead.company);
                addElement(document, voucher_head, "voucher_type", voucherHead.voucherType);
                addElement(document, voucher_head, "fiscal_year", voucherHead.fiscalYear);
                addElement(document, voucher_head, "accounting_period", voucherHead.accountingPeriod);
                addElement(document, voucher_head, "voucher_id", voucherHead.voucherId);
                addElement(document, voucher_head, "attachment_number", voucherHead.attachmentNumber);
                addElement(document, voucher_head, "date", voucherHead.prepareddate);//TODO,考虑到上传延后，是否需要修改为当前日期。
                //TODO,现在考虑的是，如果有任何失败发生，都人工介入：比如手工修改DB记录
                addElement(document, voucher_head, "enter", voucherHead.enter);
                addElement(document, voucher_head, "cashier", voucherHead.cashier);
                addElement(document, voucher_head, "signature", voucherHead.signature);
                addElement(document, voucher_head, "checker", voucherHead.checker);
                addElement(document, voucher_head, "posting_date", voucherHead.postingDate);
                addElement(document, voucher_head, "posting_person", voucherHead.postingPerson);
                addElement(document, voucher_head, "voucher_making_system", voucherHead.voucherMakingSystem);
                addElement(document, voucher_head, "memo1", voucherHead.memo1);
                addElement(document, voucher_head, "memo2", voucherHead.memo2);
                addElement(document, voucher_head, "reserve1", voucherHead.reserve1);
                addElement(document, voucher_head, "reserve2", voucherHead.reserve2);
                addElement(document, voucher_head, "revokeflag", voucherHead.revokeflag);

                //构造body
                Element voucher_body = document.createElement("voucher_body");
                voucher.appendChild(voucher_body);
                for (VoucherEntry voucherEntry : voucherHead.voucherEntryList) {
                    //构造entry
                    Element entry = document.createElement("entry");
                    voucher_body.appendChild(entry);
                    //构造各子节点
                    addElement(document, entry, "entry_id", voucherEntry.entryId);
                    addElement(document, entry, "account_code", voucherEntry.accountCode);
                    addElement(document, entry, "abstract", voucherEntry.abstracts);
                    addElement(document, entry, "settlement", voucherEntry.settlement);
                    addElement(document, entry, "bankcode", "");
                    addElement(document, entry, "notetype", "");
                    addElement(document, entry, "document_id", voucherEntry.documentId);
                    addElement(document, entry, "document_date", voucherEntry.documentDate);
                    addElement(document, entry, "currency", voucherEntry.currency);
                    addElement(document, entry, "unit_price", voucherEntry.unitPrice);
                    addElement(document, entry, "exchange_rate1", voucherEntry.exchangeRate1);
                    addElement(document, entry, "exchange_rate2", voucherEntry.exchangeRate2);
                    addElement(document, entry, "debit_quantity", voucherEntry.debitQuantity);
                    addElement(document, entry, "primary_debit_amount", voucherEntry.primaryDebitAmount);
                    addElement(document, entry, "secondary_debit_amount", voucherEntry.secondaryDebitAmount);
                    addElement(document, entry, "natural_debit_currency", voucherEntry.naturalDebitCurrency);
                    addElement(document, entry, "credit_quantity", voucherEntry.creditQuantity);
                    addElement(document, entry, "primary_credit_amount", voucherEntry.primaryCreditAmount);
                    addElement(document, entry, "secondary_credit_amount", voucherEntry.secondaryCreditAmount);
                    addElement(document, entry, "natural_credit_currency", voucherEntry.naturalCreditCurrency);
                    addElement(document, entry, "bill_type", voucherEntry.billType);
                    addElement(document, entry, "bill_id", voucherEntry.billId);
                    addElement(document, entry, "bill_date", voucherEntry.billDate);
                    if (!ComUtil.isEmpty(voucherEntry.auxiliaryAccounting)) {
                        @SuppressWarnings("unchecked")
                        List<Map<String, String>> auxiliaryAccountingList = (List<Map<String, String>>)JSON.parse(voucherEntry.auxiliaryAccounting);
                        //构造auxiliary_accounting
                        Element auxiliary_accounting = document.createElement("auxiliary_accounting");
                        entry.appendChild(auxiliary_accounting);
                        for (Map<String, String> item : auxiliaryAccountingList) {
                            //构造item
                            Element itemNode = document.createElement("item");
                            auxiliary_accounting.appendChild(itemNode);
                            itemNode.setAttribute("name", item.get("key"));
                            itemNode.appendChild(document.createTextNode(item.get("value")));
                        }
                    }
                    addElement(document, entry, "detail", voucherEntry.detail);
                }//end of for (VoucherEntry voucherEntry

                //转化保存
                String path ="D:\\办公文件";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                File file = new File(dir, voucherHead.voucherNodeId + ".xml");
                File fileRes = new File(dir, voucherHead.voucherNodeId + "_res.xml");
                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                transformer.transform(new DOMSource(document), new StreamResult(baos));
                byte[] xmlBytes = baos.toByteArray();
                baos.close();
                Files.write(file.toPath(), xmlBytes);
                //发送文件
                /*String companyName = voucherHead.company;
                String branchCode = PropertyUtil.getProperty(companyName);
                log.info("branchCode=" + branchCode);

                String response = ComUtil.httpPost(PropertyUtil.getProperty("invoice.voucher.nc.url.product") + branchCode, new String(xmlBytes, "utf-8"));
                *//*		String response = ComUtil.httpPost(PropertyUtil.getProperty("invoice.voucher.nc.url"), new String(xmlBytes, "utf-8"));*//*
                byte[] xmlBytesRes = response.getBytes("utf-8");
                Files.write(fileRes.toPath(), xmlBytesRes);
                //解析
                ByteArrayInputStream bais = new ByteArrayInputStream(xmlBytesRes);
                Document documentRes = builder.parse(bais);
                NodeList nodeList = documentRes.getElementsByTagName("resultcode");*/
                //更新状态
                voucherHead.voucherStatus = 9;//凭证状态(0:未上传，8:上传成功，9:上传失败)
               /* for (int i = 0; i < nodeList.getLength();) {
                    Node node = nodeList.item(i);
                    if (!"1".equals(node.getTextContent())) {
                        log.warn("凭证返回错误信息:" + response);
                        voucherHead.voucherStatus = 9;//凭证状态(0:未上传，8:上传成功，9:上传失败)
                    } else {
                        voucherHead.filed1 = ((Element)node.getParentNode()).getElementsByTagName("content").item(0).getTextContent();//2018.03-记账凭证-2
                        voucherHead.voucherStatus = 1;//凭证状态(0:未上传，8:上传成功，9:上传失败)
                    }
                    break;//只有一个节点
                }*/
            } catch (Exception ex) {
                log.error("凭证上传失败,headId=" + headId, ex);
                voucherHead.voucherStatus = 9;//凭证状态(0:未上传，8:上传成功，9:上传失败)
            }
            voucherHead.updateTime = new Date();
            //voucherHeadDao.updateSelective(voucherHead);
        } catch (Exception ex) {
            log.error("凭证上传处理异常,headId=" + headId, ex);
        }
        //生成xml end
        log.info("发票数据上传。。。结束。headId=" + headId);
    }

    private void addElement(Document document, Element parentElement, String tagName, Object value) {
        Element childElement = document.createElement(tagName);
        if (value == null) {
            childElement.appendChild(document.createTextNode(""));
        } else if (value instanceof BigDecimal) {
            childElement.appendChild(document.createTextNode(((BigDecimal)value).toPlainString()));
        } else {
            childElement.appendChild(document.createTextNode(String.valueOf(value)));
        }
        parentElement.appendChild(childElement);
    }

    public void uploadVoucherData() {
        log.info("发票数据上传。。。");
        List<VoucherHead> voucherList = voucherHeadMapper.getVoucherHeadList(0);//凭证状态(0:未上传，8:上传成功，9:上传失败)
        for (VoucherHead voucherHead : voucherList) {
            uploadVoucherDataOne(voucherHead.id);
        }
        log.info("发票数据上传。。。。。。结束(数量=" + voucherList.size() + ")");
    }
}